#include <cstdlib>
#include <iostream>
#include <string>
#include <string.h>
#include "ClientSocket.h"
#include "ServerSocket.h"
#include "SocketException.h"
#include "PacketFormat.h"
#include "ClientChallenge.h"
#include "Challenge.h"
#include "Response.h"
#include "Result.h"

#define HOST "localhost"
#define PORT 12345

using namespace std;

int invokeClient (string secret)
{
	try{
		ClientSocket client_socket ( HOST , PORT );
                cout << "Client started: connected to " << HOST << ":" << PORT << "\n\n";

		try{
			//client sends challenge to server
			ClientChallenge clientChallenge;
			string str_client_challenge = clientChallenge.compilePacket();

			//client receives challenge from server
			string str_challenge_from_server;
			string str_result_from_server;

			int count = 0;
			//because challenges will be sent from server until the server receives response from client
			string tmp;
                        cout << "Sending client challenge...";
			client_socket << str_client_challenge;
                        cout << "Sent!\n";
                        while(true){
				client_socket >> tmp;
                                cout << "Received: ";
				PacketFormat p(tmp);
				if (p.getCode() == 1 && count == 0){
                                    cout << "server challenge!\n";
					count++;
					str_challenge_from_server = tmp;

					//respond to server
					Response response_to_server(str_challenge_from_server, secret, clientChallenge.getChallenge());
					string str_response_to_server = response_to_server.compilePacket();
					cout << "Sending client response...";
                                        client_socket << str_response_to_server;
                                        cout << "Sent!\n";
				}
                                else if (p.getCode() == 3){
                                        cout << "Authentication success!\n";
					break;
				}
                                else if (p.getCode() == 4){
                                        cout << "Authentication failure...\n";
					break;
				}
			}
		}
		catch ( SocketException& ) {}
		//cout << "We received this response from the server:\n\"" << reply << "\"\n";;
	}
	catch ( SocketException& e ){
		cout << "Error: " << e.description() << "\n";
    }
	return 0;
}

int invokeServer (string secret)
{
	try{
	// Create the socket
	ServerSocket server ( PORT );
        cout << "\nServer started: listening on port " << PORT <<"\n\n";
        bool challenge_sent_flag=false;
        Challenge* challenge_to_client=NULL;
        Result* server_result=NULL;
        string message;

        while ( true ){
                ServerSocket new_sock;
                cout << "Waiting for incoming connections...\n";
                server.accept ( new_sock );
                
                try {

                    while ( true ) {
                        new_sock >> message; //server receives client challenge

                        if (!message.empty()) {

                            cout << "Received: ";

                            if (message[0]==0) { // client challenge
                                cout << "client challenge!\n";
                                challenge_to_client = new Challenge(message);
                                message=challenge_to_client->compilePacket();
                                cout << "Sending server challenge...";
                                new_sock << message;
                                cout << "Sent!\n";
                                message.clear();
                            }
                            else if (message[0]==2 and challenge_to_client!=NULL) {
                                cout << "client response!\n";
                                server_result=new Result(message,secret,challenge_to_client->getChallenge(),challenge_to_client->getId());
                                message=server_result->compilePacket();
                                cout << "Sending result...";
                                new_sock << message;
                                cout << "Sent!\n";
                                cout << "Authentication " << (message[0]==3?"succeed!":"failed...") << "\n\n";
                                message.clear();
                                delete challenge_to_client;
                                delete server_result;
                                challenge_to_client=NULL;
                                server_result=NULL;
                                break;
                                new_sock.~ServerSocket();
                            }
                            else cout << "Unknown message.\n\n";

                        }

                    }

                }catch ( SocketException& e ){
                        cout << "Error: the client unexpectedly closed the connection\n\n";
                }
                
		}
	}
	catch ( SocketException& e ){
		cout << "Error: " << e.description() << "\n";
    }

  return 0;
}

int main(int argc, char** argv) {
    
    string secret;
    
    if (argc>2 && !strcmp(argv[1],"-s")) {
        secret.assign(argv[2]);
        invokeServer(secret);
    }
    else if (argc>1 && !strcmp(argv[1],"-c")) {
        secret.assign(argv[2]);
        invokeClient(secret);
    }
    else
        puts("You should specify: \n-s <secret> for server \n-c <secret> for client");
    
    return 0;
}

